I am trying to generated a dynamic list of checkboxes that will be data bound to the Guid property from a Dictionary<Guid, string> variable. Then I have a separate "Select All" checkbox which will select all the checkboxes in the list. The IDs from the selected checkboxes will be stored a variable to be used in a query. The below code seems to set the IsSelected property of each checkbox just fine when I am debugging, but the screen is not re-rendering to display the new state. I am calling Invoke in the property setter, and calling StateHasChanged() in the parent component, but still there is no visible change. What am I missing?
ParentComponent.razor
<CustomCheckbox Label="Select All" @bind-Value="@IsAllBranchesSelected" @onchange="SelectAllBranchesChanged" style="margin-bottom:25px" /><hr style="margin: 0px 0px 3px 0px;" />@if (AdvancedSearchBranches != null && AdvancedSearchBranches.Any()){ @if (Checkboxes != null && Checkboxes.Any()) { @foreach (CheckBoxItemComponent cb in Checkboxes) {<CheckBoxItemComponent Value="@cb.Value" Text="@cb.Text" OnChange="Changed" /> } } }@code {public List<string> AdvancedSearchBranchIds = new List<string>();public Dictionary<Guid, string> AdvancedSearchBranches = [];public bool? IsAllBranchesSelected = false;List<CheckBoxItemComponent> Checkboxes = new List<CheckBoxItemComponent>();protected override async Task OnInitializedAsync(){ LoadData();}private async void LoadData(){ await GetBranchesAsync(); StateHasChanged();}private async Task GetBranchesAsync(){ if (AdvancedSearchBranches.Any()) { return; } Dictionary<Guid, string> _branches = await MyData.GetBranchesByCategoryAsync(Category); AdvancedSearchBranches = _branches; // create place holders in the list that will be replaced by the actual controls when the page renders foreach (var item in AdvancedSearchBranches) { Checkboxes.Add(new CheckBoxItemComponent() { Value = item.Key.ToString(), Text = item.Value, Checked = false }); }} public async Task SelectAllBranchesChanged(ChangeEventArgs e) { //select or unselect all bool isSelected = e.Value as bool? ?? false; foreach (var item in AdvancedSearchBranches) { string branchId = item.Key.ToString(); CheckBoxItemComponent? x = Checkboxes.Where(c => c.Value == branchId).FirstOrDefault(); if (x != null) { // This is getting set correctly, just not rendering x.Checked = isSelected; if (x.Checked) { if (!AdvancedSearchBranchIds.Contains(branchId)) { AdvancedSearchBranchIds.Add(branchId); } } else { if (AdvancedSearchBranchIds.Contains(branchId)) { AdvancedSearchBranchIds.Remove(branchId); } } } } StateHasChanged(); } public void Changed() { foreach (var item in AdvancedSearchBranches) { string branchId = item.Key.ToString(); CheckBoxItemComponent? x = Checkboxes.Where(c => c.Value == branchId).FirstOrDefault(); if (x != null) { x.Checked = !x.Checked; if (x.Checked) { if (!AdvancedSearchBranchIds.Contains(branchId)) { AdvancedSearchBranchIds.Add(branchId); } } else { if (AdvancedSearchBranchIds.Contains(branchId)) { AdvancedSearchBranchIds.Remove(branchId); } } } } }}CheckBoxItemComponent.razor
<input type="checkbox" @bind="@Checked" />@Text<br />@code{ [Parameter] public string Value { get; set; } [Parameter] public string Text { get; set; } [Parameter] public Action? OnChange { get; set; } private bool _Checked = false; public bool Checked { get { return _Checked; } set { _Checked = value; OnChange?.Invoke(); } }}`