Blazor模板值未更新问题:自定义网格复选框状态不同步
问题描述
我正在开发一个自定义网格组件,每行包含一个复选框,复选框状态决定行的CSS类。但当复选框状态改变时,Test.razor中显示的row.Checked值始终不更新——即使调试时该值实际是正确的,界面上一直显示false。相关代码如下:
Index.razor
<Test Items="Transactions"> <TestBody Context="row"> <TestCell Row=row></TestCell> </TestBody> </Test>
Test.razor
@typeparam TItem @attribute [CascadingTypeParameter(nameof(TItem))]; <CascadingValue Value="this" IsFixed="true"> <table> <tbody> @foreach (var row in Rows) { <tr @key="row"> @TestBody(row) <span>@row.Checked</span> // .. this does not get updated </tr> } </tbody> </table> </CascadingValue>
Test.razor.cs
namespace Accounting.Web.Components.Test { public class TestRow<TItem> { public TItem Item { get; set; } = default!; public int Id { get; set; } = default!; public bool Checked = false; } public partial class Test<TItem> { [Parameter] public IList<TItem> Items { get; set; } = default!; [Parameter] public RenderFragment<TestRow<TItem>> TestBody { get; set; } = default!; public List<TestRow<TItem>> Rows { get; set; } = new(); protected override void OnInitialized() { int i = 1; foreach (var item in Items) { var gridRow = new TestRow<TItem>() { Id = i, Item = item, }; Rows.Add(gridRow); i++; } } } }
TestCell.razor
@typeparam TItem <input type="checkbox" @onchange="eventArgs => { OnCheckChanged(eventArgs.Value); }" />
TestCell.razor.cs
namespace Accounting.Web.Components.Test { public partial class TestCell<TItem> { [Parameter] public TestRow<TItem> Row { get; set; } = default!; [Parameter] public RenderFragment ChildContent { get; set; } = default!; public void OnCheckChanged(object checkedValue) { System.Console.WriteLine((bool)checkedValue); // this reflects the correct state of the checkbox Row.Checked = (bool)checkedValue; StateHasChanged(); } } }
问题原因与修复方案
1. TestRow中的Checked是字段而非属性
Blazor的UI更新依赖属性变更通知机制,但你定义的Checked是字段(public bool Checked = false;),不是带get/set的属性。Blazor无法检测到字段的变化,因此Test.razor的UI不会更新。
修复:将Checked改为属性:
public class TestRow<TItem> { public TItem Item { get; set; } = default!; public int Id { get; set; } = default!; public bool Checked { get; set; } = false; // 改为属性 }
2. 仅在TestCell中调用StateHasChanged()无效
TestCell的状态更新只会刷新自身组件,不会通知父组件Test。必须触发Test组件的状态更新,才能让它重新渲染并显示最新的Checked值,有两种方案可选:
方案一:通过事件通知父组件
在TestCell中定义事件,复选框状态改变时触发事件,让Test组件接收并更新自身状态:
修改TestCell.razor.cs:
public partial class TestCell<TItem> { [Parameter] public TestRow<TItem> Row { get; set; } = default!; [Parameter] public RenderFragment ChildContent { get; set; } = default!; [Parameter] public EventCallback OnCheckedChanged { get; set; } // 新增事件 public void OnCheckChanged(object checkedValue) { Row.Checked = (bool)checkedValue; OnCheckedChanged.InvokeAsync(); // 触发事件通知父组件 StateHasChanged(); } }
在Index.razor中绑定事件到Test组件的状态更新方法:
<Test Items="Transactions" @ref="testComponent"> <TestBody Context="row"> <TestCell Row=row OnCheckedChanged="() => testComponent.StateHasChanged()"></TestCell> </TestBody> </Test> @code { private Test<TransactionType>? testComponent; // 替换TransactionType为你的实际数据类型 }
方案二:让TestRow实现INotifyPropertyChanged
通过实现接口来主动通知属性变更,Blazor会自动监听并更新UI:
修改TestRow:
using System.ComponentModel; using System.Runtime.CompilerServices; public class TestRow<TItem> : INotifyPropertyChanged { public TItem Item { get; set; } = default!; public int Id { get; set; } = default!; private bool _checked; public bool Checked { get => _checked; set { if (_checked != value) { _checked = value; OnPropertyChanged(); } } } public event PropertyChangedEventHandler? PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
这种方式无需手动调用StateHasChanged(),Blazor会自动处理UI更新。
3. 额外优化:复选框双向绑定
当前复选框未与Row.Checked双向绑定,初始状态不会同步Row.Checked的值。建议改为双向绑定简化代码:
<input type="checkbox" @bind="Row.Checked" @bind:event="onchange" />
这样可以直接删除OnCheckChanged方法,同时保证复选框状态与Row.Checked始终一致。
内容的提问来源于stack exchange,提问作者Musaffar Patel
相关产品推荐
相关产品推荐

