ASP.NET Core Blazor 表单绑定
注意
此版本不是本文的最新版本。 有关当前版本,请参阅本文的 .NET 9 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 对于当前版本,请参阅此文的 .NET 8 版本。
本文介绍如何在表单中使用 Blazor 绑定。
EditForm
/EditContext
模型
EditForm 基于分配的对象创建了 EditContext,用作窗体中其他组件的级联值。 EditContext 跟踪有关编辑进程的元数据,其中包括已修改的窗体字段和当前的验证消息。 分配给 EditForm.Model 或 EditForm.EditContext 可以将窗体绑定到数据。
模型绑定
分配给 EditForm.Model:
<EditForm ... Model="Model" ...>
...
</EditForm>
@code {
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
}
<EditForm ... Model="Model" ...>
...
</EditForm>
@code {
public Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
}
注意
本文的大部分窗体模型示例将窗体绑定到 C# 属性,但也支持 C# 字段绑定。
上下文绑定
分配给 EditForm.EditContext:
<EditForm ... EditContext="editContext" ...>
...
</EditForm>
@code {
private EditContext? editContext;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
}
}
<EditForm ... EditContext="editContext" ...>
...
</EditForm>
@code {
private EditContext? editContext;
public Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
}
}
将 EditContext 或 Model 分配给 EditForm。 如果同时分配了两者,则会引发运行时错误。
支持的类型
绑定支持:
- 基元类型
- 集合
- 复杂类型
- 递归类型
- 具有构造函数的类型
- 枚举
还可以使用 [DataMember]
和 [IgnoreDataMember]
属性来自定义模型绑定。 使用这些属性可重命名属性、忽略属性,并根据需要标记属性。
其他绑定选项
调用 AddRazorComponents 时,可从 RazorComponentsServiceOptions 获取其他模型绑定选项:
- MaxFormMappingCollectionSize:窗体集合中允许的最大元素数。
- MaxFormMappingRecursionDepth:递归映射窗体数据时允许的最大深度。
- MaxFormMappingErrorCount:映射窗体数据时允许的最大错误数。
- MaxFormMappingKeySize:用于读取窗体数据键的缓冲区的最大大小。
下面演示框架分配的默认值:
builder.Services.AddRazorComponents(options =>
{
options.FormMappingUseCurrentCulture = true;
options.MaxFormMappingCollectionSize = 1024;
options.MaxFormMappingErrorCount = 200;
options.MaxFormMappingKeySize = 1024 * 2;
options.MaxFormMappingRecursionDepth = 64;
}).AddInteractiveServerComponents();
窗体名称
使用 FormName 参数分配窗体名称。 窗体名称必须是唯一的,才能绑定模型数据。 以下窗体命名为 RomulanAle
:
<EditForm ... FormName="RomulanAle" ...>
...
</EditForm>
提供窗体名称:
- 对于静态呈现的服务器端组件提交的所有表单都是必需的。
- 对于交互式呈现组件提交的表单(包括 Blazor WebAssembly 应用程序中的表单和具有交互式呈现模式的组件)则不是必需的。 但是,我们建议为每个表单提供唯一的表单名称,以防止表单交互性下降时出现运行时表单发布错误。
仅当表单作为来自静态呈现的服务器端组件的传统 HTTP POST 请求发布到终结点时,才会检查表单名称。 框架不会在呈现窗体时引发异常,但仅在 HTTP POST 到达且未指定窗体名称时引发异常。
应用的根组件上方有一个未命名(空字符串)表单范围,当应用中未发生表单名称冲突时,该范围就足够了。 如果可能发生表单名称冲突,例如当包含库中的表单并且无法控制库开发人员使用的表单名称时,请在 Blazor Web App' 中提供带有 FormMappingScope 组件的表单名称范围的主要项目。
在以下示例中,HelloFormFromLibrary
组件具有名为 Hello
的表单,并且位于库中。
HelloFormFromLibrary.razor
:
<EditForm FormName="Hello" Model="this" OnSubmit="Submit">
<InputText @bind-Value="Name" />
<button type="submit">Submit</button>
</EditForm>
@if (submitted)
{
<p>Hello @Name from the library's form!</p>
}
@code {
bool submitted = false;
[SupplyParameterFromForm]
private string? Name { get; set; }
private void Submit() => submitted = true;
}
以下 NamedFormsWithScope
组件使用库的 HelloFormFromLibrary
组件,并且还有一个名为 Hello
的表单。 对于 HelloFormFromLibrary
组件提供的任何表单,FormMappingScope 组件的范围名称为 ParentContext
。 尽管本示例中的两个表单都具有表单名称 (Hello
),但表单名称不会冲突,并且事件将路由到表单 POST 事件的正确表单。
NamedFormsWithScope.razor
:
@page "/named-forms-with-scope"
<div>Hello form from a library</div>
<FormMappingScope Name="ParentContext">
<HelloFormFromLibrary />
</FormMappingScope>
<div>Hello form using the same form name</div>
<EditForm FormName="Hello" Model="this" OnSubmit="Submit">
<InputText @bind-Value="Name" />
<button type="submit">Submit</button>
</EditForm>
@if (submitted)
{
<p>Hello @Name from the app form!</p>
}
@code {
bool submitted = false;
[SupplyParameterFromForm]
private string? Name { get; set; }
private void Submit() => submitted = true;
}
提供窗体中的参数 ([SupplyParameterFromForm]
)
[SupplyParameterFromForm]
属性指示应从窗体的窗体数据中提供关联属性的值。 与属性名称匹配的请求中的数据绑定到该属性。 基于 InputBase<TValue>
的输入生成与 Blazor 用于模型绑定的名称匹配的窗体值名称。 与组件参数属性 ([Parameter]
) 不同,使用 [SupplyParameterFromForm]
注释的属性无需标记为 public
。
可以将以下窗体绑定参数指定到 [SupplyParameterFromForm]
属性:
- Name:获取或设置参数的名称。 该名称用于确定要用于匹配窗体数据的前缀,并确定是否需要绑定值。
- FormName:获取或设置句柄的名称。 该名称用于按窗体名称将参数与窗体匹配,以确定是否需要绑定值。
以下示例按窗体名称单独将两个窗体绑定到其模型。
Starship6.razor
:
@page "/starship-6"
@inject ILogger<Starship6> Logger
<EditForm Model="Model1" OnSubmit="Submit1" FormName="Holodeck1">
<div>
<label>
Holodeck 1 Identifier:
<InputText @bind-Value="Model1!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
<EditForm Model="Model2" OnSubmit="Submit2" FormName="Holodeck2">
<div>
<label>
Holodeck 2 Identifier:
<InputText @bind-Value="Model2!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
[SupplyParameterFromForm(FormName = "Holodeck1")]
private Holodeck? Model1 { get; set; }
[SupplyParameterFromForm(FormName = "Holodeck2")]
private Holodeck? Model2 { get; set; }
protected override void OnInitialized()
{
Model1 ??= new();
Model2 ??= new();
}
private void Submit1() => Logger.LogInformation("Submit1: Id={Id}", Model1?.Id);
private void Submit2() => Logger.LogInformation("Submit2: Id={Id}", Model2?.Id);
public class Holodeck
{
public string? Id { get; set; }
}
}
@page "/starship-6"
@inject ILogger<Starship6> Logger
<EditForm Model="Model1" OnSubmit="Submit1" FormName="Holodeck1">
<div>
<label>
Holodeck 1 Identifier:
<InputText @bind-Value="Model1!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
<EditForm Model="Model2" OnSubmit="Submit2" FormName="Holodeck2">
<div>
<label>
Holodeck 2 Identifier:
<InputText @bind-Value="Model2!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
[SupplyParameterFromForm(FormName = "Holodeck1")]
private Holodeck? Model1 { get; set; }
[SupplyParameterFromForm(FormName = "Holodeck2")]
private Holodeck? Model2 { get; set; }
protected override void OnInitialized()
{
Model1 ??= new();
Model2 ??= new();
}
private void Submit1() => Logger.LogInformation("Submit1: Id={Id}", Model1?.Id);
private void Submit2() => Logger.LogInformation("Submit2: Id={Id}", Model2?.Id);
public class Holodeck
{
public string? Id { get; set; }
}
}
嵌套和绑定窗体
以下指南演示如何嵌套和绑定子窗体。
以下船舶详细信息类 (ShipDetails
) 包含子窗体的说明和长度。
ShipDetails.cs
:
namespace BlazorSample;
public class ShipDetails
{
public string? Description { get; set; }
public int? Length { get; set; }
}
namespace BlazorSample;
public class ShipDetails
{
public string? Description { get; set; }
public int? Length { get; set; }
}
以下 Ship
类为标识符 (Id
) 命名,并包括船舶详细信息。
Ship.cs
:
namespace BlazorSample
{
public class Ship
{
public string? Id { get; set; }
public ShipDetails Details { get; set; } = new();
}
}
namespace BlazorSample
{
public class Ship
{
public string? Id { get; set; }
public ShipDetails Details { get; set; } = new();
}
}
以下子窗体用于编辑 ShipDetails
类型的值。 这是通过继承组件顶部的 Editor<T> 来实现的。 Editor<T> 确保子组件根据模型 (T
) 生成正确的窗体字段名称,以下示例中的 T
是 ShipDetails
。
StarshipSubform.razor
:
@inherits Editor<ShipDetails>
<div>
<label>
Description:
<InputText @bind-Value="Value!.Description" />
</label>
</div>
<div>
<label>
Length:
<InputNumber @bind-Value="Value!.Length" />
</label>
</div>
@inherits Editor<ShipDetails>
<div>
<label>
Description:
<InputText @bind-Value="Value!.Description" />
</label>
</div>
<div>
<label>
Length:
<InputNumber @bind-Value="Value!.Length" />
</label>
</div>
主窗体绑定到 Ship
类。 StarshipSubform
组件用于编辑作为 Model!.Details
绑定的船舶详细信息。
Starship7.razor
:
@page "/starship-7"
@inject ILogger<Starship7> Logger
<EditForm Model="Model" OnSubmit="Submit" FormName="Starship7">
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<StarshipSubform @bind-Value="Model!.Details" />
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
[SupplyParameterFromForm]
private Ship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() =>
Logger.LogInformation("Id = {Id} Desc = {Description} Length = {Length}",
Model?.Id, Model?.Details?.Description, Model?.Details?.Length);
}
@page "/starship-7"
@inject ILogger<Starship7> Logger
<EditForm Model="Model" OnSubmit="Submit" FormName="Starship7">
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<StarshipSubform @bind-Value="Model!.Details" />
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
[SupplyParameterFromForm]
private Ship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() =>
Logger.LogInformation("Id = {Id} Desc = {Description} Length = {Length}",
Model?.Id, Model?.Details?.Description, Model?.Details?.Length);
}
使用静态 SSR 初始化表单数据
当组件采用静态 SSR 时,OnInitialized{Async}
生命周期方法和 OnParametersSet{Async}
生命周期方法会在组件最初呈现和每次表单 POST 到服务器时触发。 若要初始化表单模型值,请在 OnParametersSet{Async}
中分配新模型值之前确认模型是否已包含数据,如以下示例所示。
StarshipInit.razor
:
@page "/starship-init"
@inject ILogger<StarshipInit> Logger
<EditForm Model="Model" OnValidSubmit="Submit" FormName="StarshipInit">
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
protected override void OnParametersSet()
{
if (Model!.Id == default)
{
LoadData();
}
}
private void LoadData()
{
Model!.Id = "Set by LoadData";
}
private void Submit()
{
Logger.LogInformation("Id = {Id}", Model?.Id);
}
public class Starship
{
public string? Id { get; set; }
}
}
高级窗体映射错误方案
框架实例化并填充窗体的 FormMappingContext,这是与给定窗体的映射操作关联的上下文。 每个映射范围(由 FormMappingScope 组件定义)实例化 FormMappingContext。 每次 [SupplyParameterFromForm]
请求值的上下文时,框架都会使用尝试的值和任何映射错误填充 FormMappingContext。
开发人员不应直接与 FormMappingContext 交互,因为它主要是 InputBase<TValue>、 EditContext 和其他内部实现的数据源,以将映射错误显示为验证错误。 在高级自定义方案中,开发人员可以直接以 [CascadingParameter]
的形式访问 FormMappingContext,以编写使用尝试的值和映射错误的自定义代码。
自定义输入组件
对于自定义输入处理方案,以下小节演示了自定义输入组件:
基于
InputBase<T>
的输入组件:该组件继承自 InputBase<TValue>,后者为绑定、回调和验证提供基础实现。 继承自 InputBase<TValue> 的组件必须在 Blazor 窗体中使用 (EditForm)。具有开发人员完全控制的输入组件:该组件完全控制输入处理。 组件的代码必须管理绑定、回调和验证。 该组件可以在 Blazor 窗体内部或外部使用。
建议从 InputBase<TValue> 中派生自定义输入组件,除非特定要求阻止你执行此操作。 该 InputBase<TValue> 类由 ASP.NET Core 团队积极维护,确保其最新 Blazor 功能和框架更保持最新状态。
基于 InputBase<T>
的输入组件
下面的示例组件执行以下操作:
- 继承自 InputBase<TValue>。 继承自 InputBase<TValue> 的组件必须在 Blazor 窗体中使用 (EditForm)。
- 从复选框获取布尔输入。
- 绑定 (
@bind:after
) 后执行AfterChange
方法时,根据复选框的状态设置其容器<div>
的背景色。 - 需要重写基类的
TryParseValueFromString
方法,但不处理字符串输入数据,因为复选框不提供字符串数据。 对于处理字符串输入的其他类型输入组件的TryParseValueFromString
的示例实现,请参阅 ASP.NET Core 参考源。
注意
指向 .NET 参考源的文档链接通常会加载存储库的默认分支,该分支表示针对下一个 .NET 版本的当前开发。 若要为特定版本选择标记,请使用“切换分支或标记”下拉列表。 有关详细信息,请参阅如何选择 ASP.NET Core 源代码的版本标记 (dotnet/AspNetCore.Docs #26205)。
EngineeringApprovalInputDerived.razor
:
@using System.Diagnostics.CodeAnalysis
@inherits InputBase<bool>
<div class="@divCssClass">
<label>
Engineering Approval:
<input @bind="CurrentValue" @bind:after="AfterChange" class="@CssClass"
type="checkbox" />
</label>
</div>
@code {
private string? divCssClass;
private void AfterChange()
{
divCssClass = CurrentValue ? "bg-success text-white" : null;
}
protected override bool TryParseValueFromString(
string? value, out bool result,
[NotNullWhen(false)] out string? validationErrorMessage)
=> throw new NotSupportedException(
"This component does not parse string inputs. " +
$"Bind to the '{nameof(CurrentValue)}' property, " +
$"not '{nameof(CurrentValueAsString)}'.");
}
要在 Starship 示例窗体 (Starship3.razor
/Starship.cs
) 中使用上述组件,请将工程审批字段的 <div>
块替换为与模型的 IsValidatedDesign
属性绑定的 EngineeringApprovalInputDerived
组件实例:
- <div>
- <label>
- Engineering Approval:
- <InputCheckbox @bind-Value="Model!.IsValidatedDesign" />
- </label>
- </div>
+ <EngineeringApprovalInputDerived @bind-Value="Model!.IsValidatedDesign" />
具有开发人员完全控件的输入组件
下面的示例组件执行以下操作:
- 不继承自 InputBase<TValue>。 该组件完全控制输入处理,包括绑定、回调和验证。 该组件可以在 Blazor 窗体内部或外部使用 (EditForm)。
- 从复选框获取布尔输入。
- 如果选中复选框,则更改背景色。
组件中的代码包括:
该
Value
属性与双向绑定一起使用,以获取或设置输入的值。ValueChanged
是更新绑定值的回调。在 Blazor 窗体中使用时:
- EditContext 是级联值。
fieldCssClass
根据 EditContext 验证结果设置字段样式。ValueExpression
是由标识绑定值的框架分配的表达式 (Expression<Func<T>>
)。- FieldIdentifier 唯一标识可以编辑的单个字段,通常对应于模型属性。 使用标识绑定值 (
ValueExpression
) 的表达式创建字段标识符。
在
OnChange
事件处理程序中:- 从 InputFileChangeEventArgs 中获取复选框输入的值。
- 设置容器
<div>
元素的背景色和文本颜色。 - EventCallback.InvokeAsync 调用与绑定关联的委托,并向使用者发送值已更改的事件通知。
- 如果在 EditForm 中使用组件(
EditContext
属性不是null
),则会调用 EditContext.NotifyFieldChanged 以触发验证。
EngineeringApprovalInputStandalone.razor
:
@using System.Globalization
@using System.Linq.Expressions
<div class="@divCssClass">
<label>
Engineering Approval:
<input class="@fieldCssClass" @onchange="OnChange" type="checkbox"
value="@Value" />
</label>
</div>
@code {
private string? divCssClass;
private FieldIdentifier fieldIdentifier;
private string? fieldCssClass => EditContext?.FieldCssClass(fieldIdentifier);
[CascadingParameter]
private EditContext? EditContext { get; set; }
[Parameter]
public bool? Value { get; set; }
[Parameter]
public EventCallback<bool> ValueChanged { get; set; }
[Parameter]
public Expression<Func<bool>>? ValueExpression { get; set; }
protected override void OnInitialized()
{
fieldIdentifier = FieldIdentifier.Create(ValueExpression!);
}
private async Task OnChange(ChangeEventArgs args)
{
BindConverter.TryConvertToBool(args.Value, CultureInfo.CurrentCulture,
out var value);
divCssClass = value ? "bg-success text-white" : null;
await ValueChanged.InvokeAsync(value);
EditContext?.NotifyFieldChanged(fieldIdentifier);
}
}
要在 Starship 示例窗体 (Starship3.razor
/Starship.cs
) 中使用上述组件,请将工程批审批段的 <div>
块替换为与模型的 IsValidatedDesign
属性绑定的 EngineeringApprovalInputStandalone
组件实例:
- <div>
- <label>
- Engineering Approval:
- <InputCheckbox @bind-Value="Model!.IsValidatedDesign" />
- </label>
- </div>
+ <EngineeringApprovalInputStandalone @bind-Value="Model!.IsValidatedDesign" />
该 EngineeringApprovalInputStandalone
组件在 EditForm 之外也可正常运行:
<EngineeringApprovalInputStandalone @bind-Value="ValidDesign" />
<div>
<b>ValidDesign:</b> @ValidDesign
</div>
@code {
private bool ValidDesign { get; set; }
}
单选按钮
本部分中的示例基于本文窗体示例部分的 Starfleet Starship Database
窗体(Starship3
组件)。
将以下 enum
类型添加到应用程序。 创建一个新文件用于保存这些类型,或将它们到 Starship.cs
文件中。
public class ComponentEnums
{
public enum Manufacturer { SpaceX, NASA, ULA, VirginGalactic, Unknown }
public enum Color { ImperialRed, SpacecruiserGreen, StarshipBlue, VoyagerOrange }
public enum Engine { Ion, Plasma, Fusion, Warp }
}
使 ComponentEnums
类可供以下项访问:
Starship.cs
中的Starship
模型(例如,using static ComponentEnums;
)。Starfleet Starship Database
窗体 (Starship3.razor
)(例如@using static ComponentEnums
)。
结合使用 InputRadio<TValue> 组件和 InputRadioGroup<TValue> 组件以创建单选按钮组。 在以下示例中,属性被添加到输入组件一文的示例表单部分所述的 Starship
模型中:
[Required]
[Range(typeof(Manufacturer), nameof(Manufacturer.SpaceX),
nameof(Manufacturer.VirginGalactic), ErrorMessage = "Pick a manufacturer.")]
public Manufacturer Manufacturer { get; set; } = Manufacturer.Unknown;
[Required, EnumDataType(typeof(Color))]
public Color? Color { get; set; } = null;
[Required, EnumDataType(typeof(Engine))]
public Engine? Engine { get; set; } = null;
更新输入组件一文的示例表单部分所述的 Starfleet Starship Database
表单(Starship3
组件)。 添加组件以生成:
- 用于选择飞船制造商的单选按钮组。
- 用于引擎和 ship 颜色的嵌套式单选按钮组。
注意
在窗体中,嵌套的单选按钮组并不常用,因为它们可能会导致窗体控件的无序布局,而这会使用户感到困惑。 不过,在某些情况下,这些在 UI 设计中是有意义的,如以下示例所示,为两个用户输入 ship 引擎和 ship 颜色配对建议。 表单验证需要一个引擎和一种颜色。 窗体的布局使用嵌套的 InputRadioGroup<TValue> 为引擎和颜色建议配对。 但是,用户可以组合任何引擎与任何颜色以提交窗体。
注意
对于以下示例,请确保 ComponentEnums
类可供组件使用:
@using static ComponentEnums
<fieldset>
<legend>Manufacturer</legend>
<InputRadioGroup @bind-Value="Model!.Manufacturer">
@foreach (var manufacturer in Enum.GetValues<Manufacturer>())
{
<div>
<label>
<InputRadio Value="manufacturer" />
@manufacturer
</label>
</div>
}
</InputRadioGroup>
</fieldset>
<fieldset>
<legend>Engine and Color</legend>
<p>
Engine and color pairs are recommended, but any
combination of engine and color is allowed.
</p>
<InputRadioGroup Name="engine" @bind-Value="Model!.Engine">
<InputRadioGroup Name="color" @bind-Value="Model!.Color">
<div style="margin-bottom:5px">
<div>
<label>
<InputRadio Name="engine" Value="Engine.Ion" />
Ion
</label>
</div>
<div>
<label>
<InputRadio Name="color" Value="Color.ImperialRed" />
Imperial Red
</label>
</div>
</div>
<div style="margin-bottom:5px">
<div>
<label>
<InputRadio Name="engine" Value="Engine.Plasma" />
Plasma
</label>
</div>
<div>
<label>
<InputRadio Name="color" Value="Color.SpacecruiserGreen" />
Spacecruiser Green
</label>
</div>
</div>
<div style="margin-bottom:5px">
<div>
<label>
<InputRadio Name="engine" Value="Engine.Fusion" />
Fusion
</label>
</div>
<div>
<label>
<InputRadio Name="color" Value="Color.StarshipBlue" />
Starship Blue
</label>
</div>
</div>
<div style="margin-bottom:5px">
<div>
<label>
<InputRadio Name="engine" Value="Engine.Warp" />
Warp
</label>
</div>
<div>
<label>
<InputRadio Name="color" Value="Color.VoyagerOrange" />
Voyager Orange
</label>
</div>
</div>
</InputRadioGroup>
</InputRadioGroup>
</fieldset>
注意
如果省略 Name
,则 InputRadio<TValue> 组件按其最新上级进行分组。
如果在输入组件一文的示例表单部分所述的 Starship3
组件中实现了上述 Razor 标记,请更新 Submit
方法的日志记录:
Logger.LogInformation("Id = {Id} Description = {Description} " +
"Classification = {Classification} MaximumAccommodation = " +
"{MaximumAccommodation} IsValidatedDesign = " +
"{IsValidatedDesign} ProductionDate = {ProductionDate} " +
"Manufacturer = {Manufacturer}, Engine = {Engine}, " +
"Color = {Color}",
Model?.Id, Model?.Description, Model?.Classification,
Model?.MaximumAccommodation, Model?.IsValidatedDesign,
Model?.ProductionDate, Model?.Manufacturer, Model?.Engine,
Model?.Color);
使用窗体中的单选按钮时,数据绑定的处理方式与其他元素不同,因为单选按钮是作为一个组进行计算的。 每个单选按钮的值是固定的,但单选按钮组的值是所选单选按钮的值。 以下示例介绍如何:
- 处理单选按钮组的数据绑定。
- 使用自定义 InputRadio<TValue> 组件支持验证。
InputRadio.razor
:
@using System.Globalization
@inherits InputBase<TValue>
@typeparam TValue
<input @attributes="AdditionalAttributes" type="radio" value="@SelectedValue"
checked="@(SelectedValue.Equals(Value))" @onchange="OnChange" />
@code {
[Parameter]
public TValue SelectedValue { get; set; }
private void OnChange(ChangeEventArgs args)
{
CurrentValueAsString = args.Value.ToString();
}
protected override bool TryParseValueFromString(string value,
out TValue result, out string errorMessage)
{
var success = BindConverter.TryConvertTo<TValue>(
value, CultureInfo.CurrentCulture, out var parsedValue);
if (success)
{
result = parsedValue;
errorMessage = null;
return true;
}
else
{
result = default;
errorMessage = "The field isn't valid.";
return false;
}
}
}
如需详细了解泛型类型参数 (@typeparam
),请参阅以下文章:
使用以下示例模型。
StarshipModel.cs
:
using System.ComponentModel.DataAnnotations;
namespace BlazorServer80
{
public class Model
{
[Range(1, 5)]
public int Rating { get; set; }
}
}
以下 RadioButtonExample
组件使用上述的 InputRadio
组件获取和验证用户的评级:
RadioButtonExample.razor
:
@page "/radio-button-example"
@using System.ComponentModel.DataAnnotations
@using Microsoft.Extensions.Logging
@inject ILogger<RadioButtonExample> Logger
<h1>Radio Button Example</h1>
<EditForm Model="Model" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
@for (int i = 1; i <= 5; i++)
{
<div>
<label>
<InputRadio name="rate" SelectedValue="i"
@bind-Value="Model.Rating" />
@i
</label>
</div>
}
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
<div>@Model.Rating</div>
@code {
public StarshipModel Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void HandleValidSubmit()
{
Logger.LogInformation("HandleValidSubmit called");
}
}