Tree with initialized checkboxes

Hello,
I'm trying to set initialized checkboxes upon loading the page.
What's the correct approach?

I use the code below, but it seems I can't modify the collection. It throws an exception.
Blazor Tree Component - Tri-state Checkboxes | Free UI Components by Radzen

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Collection was modified; enumeration operation may not execute.
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.Collections.Generic.List1.Enumerator[[Radzen.Blazor.RadzenTreeItem, Radzen.Blazor, Version=4.28.8.0, Culture=neutral, PublicKeyToken=null]].MoveNextRare() at System.Collections.Generic.List1.Enumerator[[Radzen.Blazor.RadzenTreeItem, Radzen.Blazor, Version=4.28.8.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
at Radzen.Blazor.RadzenTreeItem.Expand()
at Radzen.Blazor.RadzenTreeItem.Toggle()
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)

Thanks

If I set SingleExpand to false, I don't get the error anymore.

But !

At loading I set the checkboxes for the children only, expecting the parents to update their tristate status automatically. But it's not.
And If I open a branch containing partial checks, then the parent get its partial check status.
But if I open a branch with all its children already checked, the parent doesn't get the check.

Can you post details on how to replicate this on our demo?

For my project, I added a link to the subcategories on each categories, populated by entity framework foreign key.

In OnInitializedAsync method :

var listChecks = new List<SubCategoryDto>();
foreach (var cat in categories)
{
    listChecks.AddRange(cat.ListSubCategories.Where(sc => !sc.Excluded));
}
checkedValues = listeCheck;

On page load, subcategories are getting their checkbox ticked, but the underlying parent is not updated. Only when opening the branches, there is some update to the parents but it's not consistent, as said earlier.

Please use our demo to illustrate the problems - all demos are editable.

1 Like
@using Microsoft.EntityFrameworkCore
@using RadzenBlazorDemos.Models.Northwind

@inherits DbContextPage

<div class="container-fluid">
    <div class="row my-5">
        <div class="col-lg-6 offset-lg-3">
            <RadzenCard>
                <RadzenTree AllowCheckBoxes="true" @bind-CheckedValues=@CheckedValues Style="width: 100%; height: 300px" Data=@categories>
                    <RadzenTreeLevel TextProperty="CategoryName" ChildrenProperty="Products" />
                    <RadzenTreeLevel TextProperty="ProductName" HasChildren=@(product => false) />
                </RadzenTree>
            </RadzenCard>
        </div>
    </div>
</div>

<EventConsole @ref=@console />

@code {
    IEnumerable<Category> categories;
    IEnumerable<object> checkedValues;

    IEnumerable<object> CheckedValues
    {
        get => checkedValues;
        set
        {
            checkedValues = value;
            if (checkedValues != null)
            {
                console.Log($"CheckedValues Changed {string.Join(Environment.NewLine, value.Select(GetText))}");
            }
        }
    }

    string GetText(object data)
    {
        if (data is Category category)
        {
            return category.CategoryName;
        }

        if (data is Product product)
        {
            return product.ProductName;
        }

        return string.Empty;
    }

    EventConsole console;

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();

        categories = dbContext.Categories.Include(c => c.Products);

        var listeCheck = new List<Product>();
        foreach (var cat in categories)
        {
            listeCheck.AddRange(cat.Products.Where(p => p.ProductName.StartsWith("C")));
        }
        checkedValues = listeCheck;
    }
}

I don't see any exception with this code:

Parent will not know anything about the children checked state unless expanded.

As I said in my 2nd message, the exception is solved by setting SingleExpand to false and not adding Parent nodes to the CheckedList.

The problem I have now is with the parent checkboxes.

If you want your parent to be checked you either need to expand the children or include the parent checked state initially. The Tree component will not load anything state from children if not expanded since this will be huge performance problem.

Hum.. it's a huge stopper for me. I was so close.

Is there a way to add the Parent node as semi-checked in the checkedValues?

At the moment, no - only checked or not checked. I've just pushed however new Tree event ItemRender which will make this possible:

<RadzenTree AllowCheckBoxes="true" @bind-CheckedValues=@CheckedValues Style="width: 100%; height: 300px" Data=@categories
    ItemRender="@TreeItemRender">
    <RadzenTreeLevel TextProperty="CategoryName" ChildrenProperty="Products" />
    <RadzenTreeLevel TextProperty="ProductName" HasChildren=@(product => false) />
</RadzenTree>
...
protected override async Task OnInitializedAsync()
{
    await base.OnInitializedAsync();

    categories = dbContext.Categories.Include(c => c.Products);

    checkedValues = dbContext.Products.Where(p => p.ProductName.StartsWith("C"));
}

void TreeItemRender(TreeItemRenderEventArgs args)
{
    if (args.Value == categories.FirstOrDefault() && !args.Data.Cast<object>().Any())
    {
        args.Checked = null;
    }
}

It will be available in our next update early next week.

That's great !
Thank you very much for your help